//
//  Engine.m
//  CompGraf2
//
//  Created by Juan Niosi on 09/08/11.
//  Copyright 2011 __MyCompanyName__. All rights reserved.
//

#import "Engine.h"

@implementation Engine
@synthesize eaglView, lastUpdateTime, lastUpdateDelta;
@synthesize viewport;

- (id)initWithView:(UIView *)view
{
    self = [super init];
    if (self) {
        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();
        
        self.eaglView = (EAGLView *)view;
        CGRect bounds = view.bounds;

        glFrustumf(-0.5f, 0.5f, -0.5f, 0.5f, 1, 100);
        
        viewport = malloc(sizeof(GLfloat)*4);
        viewport[0] = 0;
        viewport[1] = 0;
        viewport[2] = bounds.size.width;
        viewport[3] = bounds.size.height;
        
        glViewport(0, 0, bounds.size.width , bounds.size.height);
        glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);
        
        glMatrixMode(GL_MODELVIEW);
        glLoadIdentity();
     
        glEnable(GL_CULL_FACE);
        glCullFace(GL_BACK);
        
        glEnable(GL_DEPTH_TEST);

        
//        glEnable(GL_FOG);
//        glFogx(GL_FOG_MODE, GL_EXP2);
//        
//        GLfloat fogColor[4];
//        fogColor[0] = 0.1f;
//        fogColor[1] = 0.1f;
//        fogColor[2] = 0.1f;
//        fogColor[3] = 1.0f;
//        glFogfv(GL_FOG_COLOR, fogColor);
        
//        glFogf(GL_FOG_DENSITY, 0.03f);
        
//        glHint(GL_FOG_HINT, GL_FASTEST);
        
        glEnable(GL_LIGHTING);
        
        
        glEnable(GL_NORMALIZE);
//        glEnable(GL_RESCALE_NORMAL);
        
        startDate = [NSDate date];
        [startDate retain];
        lastUpdateTime = 0.0f;
        lastUpdateDelta = 0.0f;
    }
    
    return self;
}

- (void)updateViewport:(UIInterfaceOrientation)orientation
{
    CGRect bounds = self.eaglView.bounds;
    if (orientation == UIInterfaceOrientationLandscapeLeft || orientation == UIInterfaceOrientationLandscapeRight)
    {
        viewport[2] = bounds.size.height;
        viewport[3] = bounds.size.width;
        glViewport(0, 0, bounds.size.height , bounds.size.width);
    }
    else
    {
        viewport[2] = bounds.size.width;
        viewport[3] = bounds.size.height;
        glViewport(0, 0, bounds.size.width , bounds.size.height);
    }
    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);

}

-(void)clear
{
    [eaglView setFramebuffer];
    
    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    
    float newTime = [[NSDate date] timeIntervalSinceDate:startDate];
    
    lastUpdateDelta = newTime - lastUpdateTime;
    lastUpdateTime = newTime;
}

- (void)update
{
    [eaglView presentFramebuffer];
}

- (void)drawGeometry:(GLfloat *)verts vertCount:(GLint)count
{
    glEnableClientState(GL_VERTEX_ARRAY);
    glVertexPointer(3, GL_FLOAT, 0, verts);
    
    glDrawArrays(GL_TRIANGLES, 0, count);
    
    glDisableClientState(GL_VERTEX_ARRAY);
}

- (void)drawGeometry:(GLfloat *)verts vertCount:(GLint)count withColor:(GLfloat *)colors
{
    glEnableClientState(GL_VERTEX_ARRAY);
    glVertexPointer(3, GL_FLOAT, 0, verts);
    
    glEnableClientState(GL_COLOR_ARRAY);
    glColorPointer(4, GL_FLOAT, 0, colors);
    
    glDrawArrays(GL_TRIANGLES, 0, count);
    
    glDisableClientState(GL_VERTEX_ARRAY);
    glDisableClientState(GL_COLOR_ARRAY);
}

- (void)drawGeometry:(GLfloat *)verts vertCount:(GLint)count withTexture:(Texture *)texture setUVs:(GLfloat *)uvs setNormals:(GLfloat *)normals;
{

    glEnableClientState(GL_NORMAL_ARRAY);
    glNormalPointer(GL_FLOAT, 0, normals);
    
    glEnableClientState(GL_TEXTURE_COORD_ARRAY);
    glBindTexture(GL_TEXTURE_2D, texture.tex_id);
    glTexCoordPointer(2, GL_FLOAT, 0, uvs);
    
    glEnable(GL_TEXTURE_2D);
    
    glEnableClientState(GL_VERTEX_ARRAY);
    glVertexPointer(3, GL_FLOAT, 0, verts);
    glDrawArrays(GL_TRIANGLES, 0, count);
    
    glDisableClientState(GL_VERTEX_ARRAY);
    glDisableClientState(GL_TEXTURE_COORD_ARRAY);
    glDisableClientState(GL_NORMAL_ARRAY);
}

- (void)drawGeometry:(GLfloat *)verts vertCount:(GLint)count withColor:(GLfloat *)colors withTexture:(Texture *)texture setUVs:(GLfloat *)uvs setNormals:(GLfloat *)normals
{
    
    glEnableClientState(GL_NORMAL_ARRAY);
    glNormalPointer(GL_FLOAT, 0, normals);
    
    glEnableClientState(GL_TEXTURE_COORD_ARRAY);
    glBindTexture(GL_TEXTURE_2D, texture.tex_id);
    glTexCoordPointer(2, GL_TEXTURE_2D, 0, uvs);
    
    glEnableClientState(GL_COLOR_ARRAY);
    glColorPointer(4, GL_FLOAT, 0, colors);
    
    glEnable(GL_TEXTURE_2D);
    
    glEnableClientState(GL_VERTEX_ARRAY);
    glVertexPointer(3, GL_FLOAT, 0, verts);
    
    glDrawArrays(GL_TRIANGLES, 0, count);
    
    glDisableClientState(GL_VERTEX_ARRAY);
    glDisableClientState(GL_COLOR_ARRAY);
    glDisableClientState(GL_NORMAL_ARRAY);
    glDisableClientState(GL_TEXTURE_COORD_ARRAY);
}

- (void)set_transform
{
    glPushMatrix();
}

- (void)reset_transform
{
    glPopMatrix();
}

- (Texture *)load_texture:(NSString *)filename
{
    UIImage *image = [UIImage imageNamed:filename]; 
    
    glEnable(GL_TEXTURE_2D);
    // Get Image size
    CGFloat width = image.size.width;
    CGFloat height = image.size.height;
    
    // Allocate memory for image
    GLubyte *imageData = malloc( height * width * 4 );
    CGContextRef imgcontext = CGBitmapContextCreate( imageData, width, height, 8, 4 * width, CGColorSpaceCreateDeviceRGB(), kCGImageAlphaPremultipliedLast);
    CGContextDrawImage( imgcontext, CGRectMake( 0, 0, width, height ), image.CGImage );
    
    // Release context
	CGContextRelease(imgcontext);
    
    // Generate texture in opengl
    GLuint tex_id;
    glGenTextures(1, &tex_id);
    glBindTexture(GL_TEXTURE_2D, tex_id);
    
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, imageData);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    
    Texture *tex = [[Texture alloc] initWithTex:imageData :tex_id];
    
    return tex;
}

- (void)translate:(GLfloat *)pos
{
    glTranslatef(pos[0], pos[1], pos[2]);
}

- (void)scale:(GLfloat *)scale
{
    glScalef(scale[0], scale[1], scale[2]);
}

- (void)translateX:(GLfloat)dist
{
    glTranslatef(dist, 0.0f, 0.0f);
}

- (void)translateY:(GLfloat)dist
{
    glTranslatef(0.0f, dist, 0.0f);
}

- (void)translateZ:(GLfloat)dist
{
    glTranslatef(0.0f, 0.0f, dist);
}

- (void)rotate:(GLfloat *)rotation
{
    glRotatef(rotation[0], 1.0f, 0.0f, 0.0f);
    glRotatef(rotation[1], 0.0f, 1.0f, 0.0f);
    glRotatef(rotation[2], 0.0f, 0.0f, 1.0f);
}

- (void)drawLight:(GLenum)glenum setPos:(GLfloat *)pos setAmbient:(GLfloat *)amb setDiffuse:(GLfloat *)dif setSpecular:(GLfloat *)spec setAttenuation:(GLfloat)attenuation
{
    glEnable(glenum);
    
    glLightfv(glenum, GL_POSITION, pos);
    glLightfv(glenum, GL_AMBIENT, amb);
    glLightfv(glenum, GL_DIFFUSE, dif);
    glLightfv(glenum, GL_SPECULAR, spec);

    
//    glLightf(glenum, GL_CONSTANT_ATTENUATION, 0.1f);
    glLightf(glenum, GL_LINEAR_ATTENUATION, attenuation);
//    glLightf(glenum, GL_QUADRATIC_ATTENUATION, 0.1f);
    
}

- (void)drawSpotLight:(GLenum)glenum setPos:(GLfloat *)pos setAmbient:(GLfloat *)amb setDiffuse:(GLfloat *)dif setSpecular:(GLfloat *)spec setAttenuation:(GLfloat)attenuation setAngle:(GLfloat)angle setDir:(GLfloat *)direction setExponent:(GLfloat)exp
{
    glEnable(glenum);
    
    glLightfv(glenum, GL_POSITION, pos);
    glLightfv(glenum, GL_AMBIENT, amb);
    glLightfv(glenum, GL_DIFFUSE, dif);
    glLightfv(glenum, GL_SPECULAR, spec);
    
    glLightf(glenum, GL_SPOT_CUTOFF, angle);
    glLightfv(glenum, GL_SPOT_DIRECTION, direction);
    glLightf(glenum, GL_SPOT_EXPONENT, exp);
    
    //    glLightf(glenum, GL_CONSTANT_ATTENUATION, 0.1f);
    glLightf(glenum, GL_LINEAR_ATTENUATION, attenuation);
    //    glLightf(glenum, GL_QUADRATIC_ATTENUATION, 0.1f);
    
}

- (void)drawBox:(GLfloat)xMin:(GLfloat)xMax:(GLfloat)yMin:(GLfloat)yMax:(GLfloat)zMin:(GLfloat)zMax
{
    GLfloat vertices[72] = 
    {
        xMin, yMin, zMin, 
        xMin, yMin, zMax,
        
        xMin, yMin, zMin, 
        xMin, yMax, zMin,
        
        xMin, yMin, zMin, 
        xMax, yMin, zMin,
        
        xMax, yMin, zMax,
        xMax, yMin, zMin,
        
        xMax, yMin, zMax,
        xMax, yMax, zMax,
        
        xMax, yMin, zMax,
        xMin, yMin, zMax,
        
        xMin, yMax, zMax,
        xMin, yMax, zMin,
        
        xMin, yMax, zMax,
        xMin, yMin, zMax,
        
        xMin, yMax, zMax,
        xMax, yMax, zMax,
        
        xMax, yMax, zMin,
        xMax, yMax, zMax,
        
        xMax, yMax, zMin,
        xMax, yMin, zMin,
        
        xMax, yMax, zMin,
        xMin, yMax, zMin,
    };
    glDisable(GL_LIGHTING);
    glDisable(GL_TEXTURE_2D);
//    glDisable(GL_FOG);
    
    glEnableClientState(GL_VERTEX_ARRAY);
    glVertexPointer(3, GL_FLOAT, 0, vertices);
    
    glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
    
    glDrawArrays(GL_LINES, 0, 24);
    
    glDisableClientState(GL_VERTEX_ARRAY);
    
    glEnable(GL_LIGHTING);
    glEnable(GL_TEXTURE_2D);
//    glEnable(GL_FOG);
}

- (void)drawLine:(GLfloat *)init:(GLfloat *)end;
{
    GLfloat vertices[6] = 
    {
        init[0], init[1], init[2], 
        end[0], end[1], end[2],
    };
    
    glDisable(GL_LIGHTING);
    glDisable(GL_TEXTURE_2D);
//    glDisable(GL_FOG);
    
    glEnableClientState(GL_VERTEX_ARRAY);
    glVertexPointer(3, GL_FLOAT, 0, vertices);
    
    glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
    
    glDrawArrays(GL_LINES, 0, 2);
    
    glDisableClientState(GL_VERTEX_ARRAY);
    
    glEnable(GL_LIGHTING);
    glEnable(GL_TEXTURE_2D);
   // glEnable(GL_FOG);
}

- (void)dealloc
{
    [eaglView release];
    [startDate release];
    free(viewport);
    [super dealloc];
}
@end
